package cn.blnp.net.flowable.ext.activity;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.*;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ExecutionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <h3></h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/20 15:33
 */
@Slf4j
@SpringBootTest
public class CallActivityFlowTest {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;
    @Resource
    private ManagementService managementService;

    /**
     *  <h2>调用活动</h2>
     *  <hr/>
     *  <p>
     *   &emsp;&emsp;调用活动一般又被称为调用子流程。概念上，子流程与调用活动在流程流转到该节点时，都会调用一个子流程实例。两者的不同之处在于，子流程嵌入原有流
     * 程定义，而调用活动是引用一个流程定义外部的流程。调用活动主要用于归纳一个有共性、可重复使用的流程定义，供多个其他流程定义调用。<br/>
     *   &emsp;&emsp;调用活动指在一个流程定义中调用另一个独立的流程定义，通常可以定义一些通用流程作为这种调用子流程，供其他多个流程定义复用。这种子流程使用
     * callActivity元素进行调用，可以方便地嵌入主流程。具体定义格式如下所示：
     * <pre>
     *     {@code
     *          <callActivity id="callactivity1" name="调用活动" flowable:calledElementType="key" calledElement="testProcessDefinitonKey">
     *              <extensionElements>
     *                  <flowable:in source="someVariableInMainProcess" target="nameOfVariableInSubProcess"/>
     *                  <flowable:out source="someVariableInSubProcss" target="nameOfVariableInMainProcess"/>
     *              </extensionElements>
     *          </callActivity>
     *     }
     * </pre>
     *  </p>
     *  <p>
     *    &emsp;&emsp;在以上调用活动定义中，通过flowable:calledElementType和calledElement属性指定被调用的外部流程定义，同时配置了向子流程传递与接收的流程变量：
     *   <ol>
     *       <li>使用flowable:in标签定义主流程流程变量传入子流程流程变量的映射，在子流程启动时复制到子流程；</li>
     *       <li>使用flowable:out标签定义子流程流程变量回传主流程流程变量的映射，在其结束时复制回主流程。</li>
     *   </ol>
     *    &emsp;&emsp;除此之外还有很多扩展属性，比如：详见：{@link src/test/java/cn/blnp/net/flowable/ext/activity/调用活动可配置属性.md}
     *  </p>
     *  <h2>案例演示</h2>
     *  <p>
     *    &emsp;&emsp;主流程为贷款申请主流程，通过其“贷款额度审批”调用活动，实现对贷款额度审批子流程的调用。
     *    <h3>设计子流程</h3>
     *    &emsp;&emsp;子流程发起后先进入“审批贷款额度”用户任务，审批完成后由排他网关进行分支决策：若贷款额度小于或等于100万元，则流程直接结束；若贷款额度大于100万元，
     *   则进入“复核贷款额度”用户任务，复核完成后流程结束。
     *      <b>流程定义文件：</b>{@link flow/approveLoanSubProcess.bpmn20.xml}<br/>
     *      <b>流程预览图：</b><br/>
     *      <img src="https://cdn4.winhlb.com/2025/05/20/682c4330e3676.png" /><br/>
     *
     *   <h3>设计主流程</h3>
     *    &emsp;&emsp;主流程发起后，流程流转到“贷款申请”用户任务节点，贷款申请提交后流程流转到“贷款额度审批”调用活动，主流程进入等待状态，启动并执行上述子流程，子流程流转结束后
     *    主流程流转到“发放贷款”用户任务节点。任务办理完成后流程结束。
     *      <b>流程定义文件：</b>{@link flow/approveLoanMainProcess.bpmn20.xml}<br/>
     *      <b>流程预览图：</b><br/>
     *      <img src="https://cdn4.winhlb.com/2025/05/20/682c453660ca6.png" /><br/>
     *  </p>
     **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                .name("调用子流程-演示案例")
                .addClasspathResource("flow/approveLoanMainProcess.bpmn20.xml")
                .addClasspathResource("flow/approveLoanSubProcess.bpmn20.xml")
                .deploy();
        log.info("部署流程定义：{}", deploy.getId());
        List<ProcessDefinition> definitionList = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .list();
        definitionList.forEach(procDef -> {
            log.info("流程定义编号：{}, 流程定义名称：{}", procDef.getId(), procDef.getName());
        });
        //设置流程变量
        Map<String, Object> varMap1 = MapUtil.builder(new HashMap<String, Object>())
                .put("lender", "张三")
                .put("subProcessBusinessKey", IdUtil.simpleUUID())
                .put("subProcessBusinessName", "张三的贷款申请")
                .build();
        //发起主流程
        ProcessInstance mainProcessInstance = runtimeService
                .startProcessInstanceByKey("approveLoanMainProcess", varMap1);
        ExecutionQuery executionQuery = runtimeService.createExecutionQuery()
                .rootProcessInstanceId(mainProcessInstance.getId());

        //根据主流程实例编号查询执行实例
        List<Execution> executionList1 = executionQuery.list();
        log.info("主流程发起后，执行实例总数为{}，分别为：{}", executionList1.size(),
                JSON.toJSONString(executionList1, executionFilter));
        //查询主流程的流程变量
        Map<String, Object> mainProcessVarMap1 = runtimeService
                .getVariables(mainProcessInstance.getId());
        log.info("主流程的流程变量为：{}", mainProcessVarMap1);
        //查询主流程第一个任务
        Task firstTaskOfMainProcess = taskService.createTaskQuery()
                .processInstanceId(mainProcessInstance.getId()).singleResult();
        log.info("主流程发起后，流程当前所在用户任务为：{}", firstTaskOfMainProcess.getName());
        //完成主流程第一个任务
        taskService.complete(firstTaskOfMainProcess.getId());
        log.info("完成用户任务：{}，启动子流程", firstTaskOfMainProcess.getName());
        //查询执行实例
        List<Execution> executionList2 = executionQuery.list();
        log.info("子流程发起后，执行实例总数为{}，分别为：{}", executionList2.size(),
                JSON.toJSONString(executionList2, executionFilter));
        //查询子流程的流程实例编号（通过callActivity的flowable:idVariableName属性指定）
        String subProcessInstanceId = (String)runtimeService
                .getVariable(mainProcessInstance.getId(), "subProcessInstanceId");
        //查询子流程第一个任务
        Task firstTaskOfSubProcess = taskService.createTaskQuery()
                .processInstanceId(subProcessInstanceId).singleResult();
        log.info("子流程发起后，子流程当前所在用户任务为：{}", firstTaskOfSubProcess.getName());
        //查询子流程的流程变量
        Map<String, Object> subProcessVarMap1 = runtimeService
                .getVariables(subProcessInstanceId);
        log.info("子流程的流程变量为：{}", subProcessVarMap1);
        //设置子流程的流程变量
        Map<String, Object> varMap2 = MapUtil.builder(new HashMap<String, Object>())
                .put("loanAmount", 100000)
                .build();
        //完成子流程第一个任务，子流程结束
        taskService.complete(firstTaskOfSubProcess.getId(), varMap2);
        log.info("完成用户任务：{}，结束子流程", firstTaskOfSubProcess.getName());
        //查询执行实例
        List<Execution> executionList3 = executionQuery.list();
        log.info("子流程结束后，回到主流程，执行实例总数为{}，分别为：{}", executionList3.size(),
                JSON.toJSONString(executionList3, executionFilter));
        Map<String, Object> mainProcessVarMap2 = runtimeService
                .getVariables(mainProcessInstance.getId());
        log.info("主流程的流程变量为：{}", mainProcessVarMap2);
        //查询主流程第二个任务
        Task secondTaskOfMainProcess = taskService.createTaskQuery()
                .processInstanceId(mainProcessInstance.getId()).singleResult();
        log.info("流程当前所在用户任务为：" + secondTaskOfMainProcess.getName());
        //完成主流程第二个任务，主流程结束
        taskService.complete(secondTaskOfMainProcess.getId());
        log.info("完成用户任务：{}，结束主流程", secondTaskOfMainProcess.getName());
        //查询执行实例
        List<Execution> executionList4 = executionQuery.list();
        log.info("主流程结束后，执行实例总数为{}，分别为：{}", executionList4.size(),
                JSON.toJSONString(executionList4, executionFilter));

        /**
         *  演示结果：
         *  2025-05-20 17:14:30.106  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 部署流程定义：d59c89f0-355a-11f0-8edb-50ebf6ec8f6d
         * 2025-05-20 17:14:30.109  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 流程定义编号：approveLoanMainProcess:1:d5e94a65-355a-11f0-8edb-50ebf6ec8f6d, 流程定义名称：审批贷款主流程
         * 2025-05-20 17:14:30.110  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 流程定义编号：approveLoanSubProcess:1:d5e97176-355a-11f0-8edb-50ebf6ec8f6d, 流程定义名称：贷款额度审批示例子流程
         * 2025-05-20 17:14:30.445  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 主流程发起后，执行实例总数为2，分别为：[{"id":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","processDefinitionKey":"approveLoanMainProcess","processInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","rootProcessInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","scope":true},{"activityId":"firstTaskOfMainProcess","id":"d5f57f6b-355a-11f0-8edb-50ebf6ec8f6d","parentId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","processDefinitionKey":"approveLoanMainProcess","processInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","rootProcessInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","scope":false}]
         * 2025-05-20 17:14:30.454  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 主流程的流程变量为：{lender=张三, subProcessBusinessKey=21921e94df8d4cdb92e3877737cc5824, subProcessBusinessName=张三的贷款申请}
         * 2025-05-20 17:14:30.464  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 主流程发起后，流程当前所在用户任务为：贷款申请
         * 2025-05-20 17:14:30.511  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 完成用户任务：贷款申请，启动子流程
         * 2025-05-20 17:14:30.514  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 子流程发起后，执行实例总数为4，分别为：[{"id":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","processDefinitionKey":"approveLoanMainProcess","processInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","rootProcessInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","scope":true},{"activityId":"callActivity1","id":"d5f57f6b-355a-11f0-8edb-50ebf6ec8f6d","parentId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","processDefinitionKey":"approveLoanMainProcess","processInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","rootProcessInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","scope":false},{"businessKey":"21921e94df8d4cdb92e3877737cc5824","id":"d6291292-355a-11f0-8edb-50ebf6ec8f6d","name":"张三的贷款申请","processDefinitionKey":"approveLoanSubProcess","processInstanceId":"d6291292-355a-11f0-8edb-50ebf6ec8f6d","rootProcessInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","scope":true,"superExecutionId":"d5f57f6b-355a-11f0-8edb-50ebf6ec8f6d"},{"activityId":"firstTaskOfSubProcess","id":"d6291296-355a-11f0-8edb-50ebf6ec8f6d","parentId":"d6291292-355a-11f0-8edb-50ebf6ec8f6d","processDefinitionKey":"approveLoanSubProcess","processInstanceId":"d6291292-355a-11f0-8edb-50ebf6ec8f6d","rootProcessInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","scope":false}]
         * 2025-05-20 17:14:30.518  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 子流程发起后，子流程当前所在用户任务为：审批贷款额度
         * 2025-05-20 17:14:30.519  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 子流程的流程变量为：{lender=张三, subProcessBusinessName=张三的贷款申请}
         * 2025-05-20 17:14:30.575  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 完成用户任务：审批贷款额度，结束子流程
         * 2025-05-20 17:14:30.578  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 子流程结束后，回到主流程，执行实例总数为2，分别为：[{"id":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","processDefinitionKey":"approveLoanMainProcess","processInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","rootProcessInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","scope":true},{"activityId":"secondTaskOfMainProcess","id":"d5f57f6b-355a-11f0-8edb-50ebf6ec8f6d","parentId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","processDefinitionKey":"approveLoanMainProcess","processInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","rootProcessInstanceId":"d5f50a37-355a-11f0-8edb-50ebf6ec8f6d","scope":false}]
         * 2025-05-20 17:14:30.581  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 主流程的流程变量为：{lender=张三, subProcessInstanceId=d6291292-355a-11f0-8edb-50ebf6ec8f6d, subProcessBusinessKey=21921e94df8d4cdb92e3877737cc5824, subProcessBusinessName=张三的贷款申请, loanAmount=100000}
         * 2025-05-20 17:14:30.583  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 流程当前所在用户任务为：发放贷款
         * 2025-05-20 17:14:30.609  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 完成用户任务：发放贷款，结束主流程
         * 2025-05-20 17:14:30.610  INFO 71324 --- [           main] c.b.n.f.e.activity.CallActivityFlowTest  : 主流程结束后，执行实例总数为0，分别为：[]
         **/
    }

    SimplePropertyPreFilter executionFilter = new SimplePropertyPreFilter(Execution.class,
            "id", "name", "businessKey", "processInstanceId", "rootProcessInstanceId",
            "superExecutionId", "scope", "parentId", "activityId", "processDefinitionKey");
}
